{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "697f0953",
   "metadata": {},
   "source": [
    "# 提取 anim 数据到 json 文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ba78df49",
   "metadata": {},
   "outputs": [],
   "source": [
    "from anim_bone import *\n",
    "\n",
    "# config = anim_config(\"data/config/anim_config.json\")\n",
    "# config.parse_and_save_data(\"data/anim/full_json/\")\n",
    "\n",
    "config = anim_config(\"data/config/simple_config.json\")\n",
    "config.parse_data()\n",
    "\n",
    "print(config.anim_data)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3b90702d",
   "metadata": {},
   "source": [
    "# 绑定骨骼动画并提取数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3bb4b98f",
   "metadata": {},
   "outputs": [],
   "source": [
    "from anim_bone import *\n",
    "import random\n",
    "\n",
    "# 注意骨骼提取顺序是由 json 文件中 bones 数组的顺序决定的\n",
    "config = anim_config(\"data/config/full_json_config.json\")\n",
    "config.parse_data()\n",
    "data = config.extract_rotation_array()\n",
    "for bone_name, values in data.items():\n",
    "    print(bone_name, len(values))\n",
    "\n",
    "np.savez(\"data/full_anim_thigh_30.npz\", **data)\n",
    "\n",
    "data = anim_config.extract_rotation_target_array(\"data/full_anim_thigh_30.npz\")\n",
    "np.savez(\"data/full_anim_thigh_target_30.npz\", **data)\n",
    "\n",
    "tup = []\n",
    "for i in range(len(data[\"Bip_L_Thigh\"])):\n",
    "    tup.append((data[\"Bip_L_Thigh\"][i], data[\"Bip_R_Thigh\"][i]))\n",
    "\n",
    "sampled_arr = random.choices(tup, k=300)  # 可能输出 [2, 2, 4]\n",
    "\n",
    "data[\"Bip_L_Thigh\"] = [x[0] for x in sampled_arr]\n",
    "data[\"Bip_R_Thigh\"] = [x[1] for x in sampled_arr]\n",
    "\n",
    "# 保存数据\n",
    "np.savez(\"data/simple_anim_thigh_data.npz\", **data)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "edf668f3",
   "metadata": {},
   "source": [
    "# 生成聚类"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "898296f0",
   "metadata": {},
   "source": [
    "## 四元数数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "f8af4e92",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Pyclustering KMedians combine clustering start.\n",
      "Pyclustering KMedians clustering 11 finished.\n",
      "Pyclustering KMedians clustering data saved in data/KMedians_quat_target_chord_dist_10_combined.npz.\n"
     ]
    }
   ],
   "source": [
    "from anim_bone import *\n",
    "\n",
    "cluster = anim_cluster(\"data/rotation/filtered_anim_thigh_data.npz\")\n",
    "\n",
    "cluster.pyclustering_cluster_combine(\n",
    "    method=\"KMedians\",\n",
    "    prefix=\"data/\",\n",
    "    num_clusters=10,\n",
    "    dist_func=anim_distance.quat_target_chord_dist,\n",
    "    dist_func_combine=anim_distance.dist_func_sum_cross_combine,\n",
    "    filter=anim_filter(poses=[quaternion.A_pose().to_array() * 2]),\n",
    "    filter_threshold=0.5,\n",
    "    enable_divide=True,\n",
    "    divide_threshold=2,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7f18aa02",
   "metadata": {},
   "source": [
    "## 位置数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "8b3148d4",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "g:\\TestCode\\Cluster\\.venv\\Lib\\site-packages\\vpython\\__init__.py:1: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81.\n",
      "  from pkg_resources import get_distribution, DistributionNotFound\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div id=\"glowscript\" class=\"glowscript\"></div>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/javascript": "if (typeof Jupyter !== \"undefined\") { window.__context = { glowscript_container: $(\"#glowscript\").removeAttr(\"id\")};}else{ element.textContent = ' ';}",
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Pyclustering KMedians combine clustering start.\n",
      "Pyclustering KMedians clustering 20 finished.\n",
      "Pyclustering KMedians clustering data saved in data/KMedians_target_chord_dist_dist_func_euclid_combine_10.npz.\n"
     ]
    }
   ],
   "source": [
    "from anim_bone import *\n",
    "\n",
    "cluster = anim_cluster(\"data/target/full_anim_thigh_target_data_30.npz\")\n",
    "\n",
    "cluster.pyclustering_cluster_combine(\n",
    "    method=\"KMedians\",\n",
    "    prefix=\"data/\",\n",
    "    num_clusters=10,\n",
    "    dist_func=anim_distance.target_chord_dist,\n",
    "    dist_func_combine=anim_distance.dist_func_euclid_combine,\n",
    "    filter=anim_filter(poses=[[-1, 0, 0] * 2]),\n",
    "    filter_threshold=0.5,\n",
    "    enable_merge=True,\n",
    "    merge_threshold=0.2,\n",
    "    enable_divide=True,\n",
    "    divide_threshold=0.5,\n",
    "    enable_recluster=True,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "49850b12",
   "metadata": {},
   "outputs": [],
   "source": [
    "from anim_bone import *\n",
    "\n",
    "data = np.load(\"data/KMedians_target_chord_dist_10_combined.npz\", allow_pickle=True)\n",
    "data = {\n",
    "            key: (data[key].item() if isinstance(data[key], np.ndarray) else data[key])\n",
    "            for key in data.files\n",
    "        }\n",
    "\n",
    "for k, v in data.items():\n",
    "    print(k, len(v[\"Clusters\"]))\n",
    "    for c in v[\"Clusters\"]:\n",
    "        print(len(c))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "67a87db0",
   "metadata": {},
   "outputs": [],
   "source": [
    "from anim_bone import *\n",
    "\n",
    "data = np.load(\"data/full_anim_thigh_and_calf_data.npz\")\n",
    "data = {k: v for k, v in data.items()}\n",
    "\n",
    "threshold = 0.3\n",
    "\n",
    "right_pose = quaternion.euler(-0.074, 178.9, -90).to_array()\n",
    "left_pose = quaternion.euler(0.074, -178.9, -90).to_array()\n",
    "right_ref_pose = quaternion.euler(0.074, 178.9, -10.318)\n",
    "left_ref_pose = quaternion.euler(0.074, -178.9, -10.318)\n",
    "target_right_leg_data = data[\"Bip_R_Thigh\"]\n",
    "target_left_leg_data = data[\"Bip_L_Thigh\"]\n",
    "right_pose_filter = []\n",
    "left_pose_filter = []\n",
    "for target in target_right_leg_data:\n",
    "    right_pose_filter.append(anim_distance.quat_target_angle_dist(target, right_pose) < threshold)\n",
    "\n",
    "for target in target_left_leg_data:\n",
    "    left_pose_filter.append(anim_distance.quat_target_angle_dist(target, left_pose) < threshold)\n",
    "\n",
    "conditional_left_pose = data[\"Bip_L_Thigh\"][right_pose_filter]\n",
    "conditional_right_pose = data[\"Bip_R_Thigh\"][left_pose_filter]\n",
    "\n",
    "total_filter = []\n",
    "for i in range(len(data[\"Bip_L_Thigh\"])):\n",
    "    total_filter.append(right_pose_filter[i] or left_pose_filter[i])\n",
    "\n",
    "new_data = {}\n",
    "new_data[\"Bip_L_Thigh\"] = data[\"Bip_L_Thigh\"][total_filter]\n",
    "new_data[\"Bip_R_Thigh\"] = data[\"Bip_R_Thigh\"][total_filter]\n",
    "\n",
    "np.savez(\"data/filtered_anim_thigh_data.npz\", **new_data)\n",
    "\n",
    "# x = cylinder(\n",
    "#     pos=vector(0, 0, 0), axis=vector(1, 0, 0), radius=0.01, color=color.red\n",
    "# )\n",
    "# y = cylinder(\n",
    "#     pos=vector(0, 0, 0), axis=vector(0, 1, 0), radius=0.01, color=color.green\n",
    "# )\n",
    "# z = cylinder(\n",
    "#     pos=vector(0, 0, 0), axis=vector(0, 0, 1), radius=0.01, color=color.blue\n",
    "# )\n",
    "\n",
    "# left_ref = cylinder(\n",
    "#     pos=vector(0, 0, 0), axis = left_ref_pose * vector(1, 0, 0), radius = 0.05, color = color.white\n",
    "# )\n",
    "\n",
    "# right_ref = cylinder(\n",
    "#     pos=vector(0, 2, 0), axis = right_ref_pose * vector(1, 0, 0), radius = 0.05, color = color.white\n",
    "# )\n",
    "\n",
    "# for pose in conditional_left_pose:\n",
    "#     q = quaternion(*pose)\n",
    "#     pos = q * vector(1, 0, 0)\n",
    "#     sphere(pos=pos, radius=0.01, color=color.red)\n",
    "\n",
    "# for pose in conditional_right_pose:\n",
    "#     q = quaternion(*pose)\n",
    "#     pos = vector(0, 2, 0) + q * vector(1, 0, 0)\n",
    "#     sphere(pos=pos, radius=0.01, color=color.green)\n",
    "\n",
    "\n",
    "\n",
    "# scene.width = 1600\n",
    "# scene.height = 800\n",
    "# current_time = time.time()\n",
    "# start_time = time.time()\n",
    "# while True:\n",
    "#     if current_time - start_time > 600:\n",
    "#         break\n",
    "#     current_time = time.time()\n",
    "#     rate(30)\n",
    "\n",
    "# right_pose = (\n",
    "#     quaternion.euler(0, 0, -90).to_array() + quaternion.euler(0, 180, 0).to_array()\n",
    "# )\n",
    "# left_pose = (\n",
    "#     quaternion.euler(0, 180, 0).to_array() + quaternion.euler(0, 0, -90).to_array()\n",
    "# )\n",
    "\n",
    "# # 合并数据\n",
    "# target_data = [data[\"Bip_R_Thigh\"], data[\"Bip_L_Thigh\"]]\n",
    "# target_data = np.concatenate(target_data, axis=1)\n",
    "\n",
    "# # 组合距离\n",
    "# dist_func = anim_distance.dist_func_combine(\n",
    "#     anim_distance.target_chord_dist, combine_num=2, single_num=4\n",
    "# )\n",
    "\n",
    "# right_pose_result = []\n",
    "# left_pose_result = []\n",
    "# for target in target_data:\n",
    "#     right_pose_result.append(dist_func(target, right_pose))\n",
    "#     left_pose_result.append(dist_func(target, left_pose))\n",
    "\n",
    "# nearest_to_right_pose = np.argmin(right_pose_result)\n",
    "# nearest_to_left_pose = np.argmin(left_pose_result)\n",
    "\n",
    "# print(f\"Nearest right distance: {right_pose_result[nearest_to_right_pose]}\")\n",
    "# print(f\"Nearest left distance: {left_pose_result[nearest_to_left_pose]}\")\n",
    "\n",
    "\n",
    "# right_pose_nearest_to_right_pose = target_data[nearest_to_right_pose][0:4]\n",
    "# left_pose_nearest_to_right_pose = target_data[nearest_to_right_pose][4:8]\n",
    "\n",
    "# right_pose_nearest_to_left_pose = target_data[nearest_to_left_pose][0:4]\n",
    "# left_pose_nearest_to_left_pose = target_data[nearest_to_left_pose][4:8]\n",
    "\n",
    "# poses = {\n",
    "#     \"Bip_R_Thigh\": {\n",
    "#         \"Centers\": [\n",
    "#             right_pose_nearest_to_right_pose,\n",
    "#             right_pose_nearest_to_left_pose,\n",
    "#             quaternion.euler(0, 180, -90).to_array(),\n",
    "#             quaternion.euler(0, 180, 0).to_array(),\n",
    "#         ],\n",
    "#     },\n",
    "#     \"Bip_L_Thigh\": {\n",
    "#         \"Centers\": [\n",
    "#             left_pose_nearest_to_right_pose,\n",
    "#             left_pose_nearest_to_left_pose,\n",
    "#             quaternion.euler(0, 180, 0).to_array(),\n",
    "#             quaternion.euler(0, 180, -90).to_array(),\n",
    "#         ],\n",
    "#     },\n",
    "# }\n",
    "\n",
    "# anim_cluster_analyzer.analyze_custom_pose(poses)\n",
    "\n",
    "# right_pose_nearest_to_right_pose = quaternion(*right_pose_nearest_to_right_pose).to_euler()\n",
    "# left_pose_nearest_to_right_pose = quaternion(*left_pose_nearest_to_right_pose).to_euler()\n",
    "# right_pose_nearest_to_left_pose = quaternion(*right_pose_nearest_to_left_pose).to_euler()\n",
    "# left_pose_nearest_to_left_pose = quaternion(*left_pose_nearest_to_left_pose).to_euler()\n",
    "\n",
    "# print(f\"Nearest Right Right: {right_pose_nearest_to_right_pose}\")\n",
    "# print(f\"Nearest Right Left: {left_pose_nearest_to_right_pose}\")\n",
    "# print(f\"Nearest Left Right: {right_pose_nearest_to_left_pose}\")\n",
    "# print(f\"Nearest Left Left: {left_pose_nearest_to_left_pose}\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "27c08103",
   "metadata": {},
   "outputs": [],
   "source": [
    "from anim_bone import *\n",
    "\n",
    "\n",
    "data = np.load(\"data/filtered_anim_thigh_data.npz\", allow_pickle=True)\n",
    "data = {k: v for k, v in data.items()}\n",
    "\n",
    "x = vector(1, 0, 0)\n",
    "rotated_left_data = []\n",
    "rotated_right_data = []\n",
    "rotated_combined_data = []\n",
    "\n",
    "for v in data[\"Bip_L_Thigh\"]:\n",
    "    q = quaternion(*v)\n",
    "    p = q * x\n",
    "    rotated_left_data.append([p.x, p.y, p.z])\n",
    "\n",
    "for v in data[\"Bip_R_Thigh\"]:\n",
    "    q = quaternion(*v)\n",
    "    p = q * x\n",
    "    rotated_right_data.append([p.x, p.y, p.z])\n",
    "\n",
    "for v1, v2 in zip(rotated_left_data, rotated_right_data):\n",
    "    rotated_combined_data.append(v1 + v2)\n",
    "\n",
    "A = np.array(rotated_combined_data)\n",
    "print(A.shape)\n",
    "\n",
    "# 进行SVD分解\n",
    "U, s, VT = np.linalg.svd(A, full_matrices=False)\n",
    "print(s)\n",
    "\n",
    "# 选择前3个奇异值对应的右奇异向量 (VT的前3行)\n",
    "k = 3\n",
    "Vk_T = VT[:k, :]  # 形状为 (3,6)\n",
    "\n",
    "# 降维投影：A_reduced = A @ Vk_T.T (1000x6 @ 6x3 = 1000x3)\n",
    "A_reduced = A @ Vk_T.T\n",
    "\n",
    "\n",
    "# for v in A_reduced:\n",
    "#     sphere(pos=vector(v[0], v[1], v[2]), radius=0.01, color=color.red)\n",
    "\n",
    "# anim_cluster_analyzer.custom_anim_loop()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "744c72a3",
   "metadata": {},
   "outputs": [],
   "source": [
    "from anim_bone import *\n",
    "\n",
    "left_centers = [\n",
    "    quaternion.euler(0, -180, -60).to_array(),\n",
    "    quaternion.euler(0, -180, -120).to_array(),\n",
    "    quaternion.euler(-60, -180, -60).to_array()\n",
    "]\n",
    "right_centers = [\n",
    "    quaternion.euler(0, 180, -60).to_array(),\n",
    "    quaternion.euler(0, 180, -120).to_array(),\n",
    "    quaternion.euler(60, 180, -60).to_array()\n",
    "]\n",
    "\n",
    "centers = []\n",
    "for left in left_centers:\n",
    "    for right in right_centers:\n",
    "        centers.append(left + right)\n",
    "\n",
    "print(centers)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7e821105",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "a = [1, 3, 4]\n",
    "b = np.array(a)\n",
    "print(type(b))\n",
    "print(b)\n",
    "\n",
    "a = [[1, 2, 3], [2, 3, 4]]\n",
    "b = np.array(a)\n",
    "print(type(b))\n",
    "print(b)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "f254fee0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.0\n",
      "<0.455842, 0.569803, 0.683763>\n",
      "<0.455842, 0.569803, 0.683763>\n"
     ]
    }
   ],
   "source": [
    "from anim_bone import *\n",
    "\n",
    "a = vector(1, 2, 3)\n",
    "b = vector(4, 5, 6)\n",
    "q = quaternion.from_to(a, b)\n",
    "print(q.magnitude2())\n",
    "print(q * a.hat)\n",
    "print(b.hat)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "58664eef",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "g:\\TestCode\\Cluster\\.venv\\Lib\\site-packages\\vpython\\__init__.py:1: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81.\n",
      "  from pkg_resources import get_distribution, DistributionNotFound\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div id=\"glowscript\" class=\"glowscript\"></div>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/javascript": "if (typeof Jupyter !== \"undefined\") { window.__context = { glowscript_container: $(\"#glowscript\").removeAttr(\"id\")};}else{ element.textContent = ' ';}",
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(0.08992190771321046, -0.9959014196174742, -0.00022866156346379267, 0.009713940035120267)\n",
      "<0.074, 181.111, 349.682>\n"
     ]
    }
   ],
   "source": [
    "from anim_bone import *\n",
    "\n",
    "q = quaternion.euler(0.074, -178.889, -10.318)\n",
    "print(q)\n",
    "\n",
    "p = q.to_euler()\n",
    "print(p)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv (3.13.5)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
